#include "application.h"

namespace Application
{

LRESULT CALLBACK handleWindowMessageProc(HWND hWnd, UINT msg, WPARAM wParam, 
                                         LPARAM lParam)
{
    /**
     * Executed on a windows message. This handles all the messages on the
     * windows event message queue. All we do is pass the message to the
     * application singleton for better handling. Since the function pointer
     * cannot point to a class object, we must handle it procedurally.
     *
     * @param HWND hWnd The window to send the message
     * @param WPARAM wParam The wide parameter, this is the message that was
     *                      sent
     * @param LPARAM lParam Any additional event information for the message.
     */
    return (CApplication::getInstance()->handleWindowMessage(
                    hWnd, msg, wParam, lParam));
}

void keyboardCallback(const DEVICE_EVENT &evt)
{
    /**
     * Executed when a key on the keyboard has been pressed. This handles all
     * keyboard messages. Must be in the global scope and handled there.
     * **TODO** If I get time, make the function pointer point to any class
     * instance.
     *
     * @Param const DEVICE_EVENT &evt The event information.
     */
    if (evt.type == DEVICE_KEYBOARD)
    {
        CKeyboard *pKeyboard(dynamic_cast<CKeyboard*>(evt.pDevice));
        if (pKeyboard->keyPressed(evt.charrKeyboardData, DIK_F10))
        {
            PostQuitMessage(0);
        }
    }
}

void mouseCallback(const DEVICE_EVENT &evt)
{
    /**
     * Executed when a mouse event has occured; this includes but not limited
     * to mouse movement and mouse clicks.
     *
     * @Param const DEVICE_EVENT &evt The event information.
     */
    if (evt.type == DEVICE_MOUSE)
    {
    }
}

void instructions(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
    unsigned char chKey)
{
    if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
    {
        CUI::getInstance()->getChild("background")->hide();
        CUI::getInstance()->getChild("instructions")->show();
        pMenu->setActive(false);
        CMenuContainer::getInstance()->getMenu("back_menu")->setActive(true);
    }
}

void credits(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
    unsigned char chKey)
{
    if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
    {
        CUI::getInstance()->getChild("background")->hide();
        CUI::getInstance()->getChild("credits")->show();
        pMenu->setActive(false);
        CMenuContainer::getInstance()->getMenu("back_menu")->setActive(true);
    }
}

void startGame(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
    unsigned char chKey)
{
    if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
    {
        pMenu->setActive(false);
        CMenuContainer::getInstance()->getMenu("map_menu")->setActive(true);
    }
}

void options(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
    unsigned char chKey)
{
    pMenu->setActive(false);
    CMenuContainer::getInstance()->getMenu("option_menu")->setActive(true);
}

void exit(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
    unsigned char chKey)
{
    if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
    {
        PostQuitMessage(0);
    }
}

} // namespace Application

namespace PauseCallbacks
{
    void continue_(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        unsigned char chKey)
    {
        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
        {
            CMapContainer::getInstance()->getCurrentMap()->resume();
            pMenu->setActive(false);
        }
    }

    void restart(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        unsigned char chKey)
    {
        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
        {
            CMapContainer::getInstance()->getCurrentMap()->reset();
            CMapLoader mapLoader;
            mapLoader.reloadMap(CMapContainer::getInstance()->getCurrentMap());
            pMenu->setActive(false);
            CUI::getInstance()->getChild("pause")->hide();
            CUI::getInstance()->getChild("end")->hide();
        }
    }

    void exit(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        unsigned char chKey)
    {
        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
        {
            CMapContainer::getInstance()->unloadCurrentMap();
            pMenu->setActive(false);

            CApplication::getInstance()->startMusic();

            CMapLoader mapLoader;
            CMap *pMap(mapLoader.loadMap("maps/demo/test.bmm", true));
            CMapContainer::getInstance()->setCurrentMap(pMap);

            CUI::getInstance()->getChild("background")->show();
            CMenuContainer::getInstance()->getMenu("main_menu")->setActive(
                true);
        }
    }
}

namespace Options
{
    void volume(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        utils::PARAM &param, unsigned char chKey)
    {
        float fVolume(CSoundManager::getInstance()->getMasterVolume());

        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN || 
            chKey == DIK_RIGHT)
        {
            if (fVolume >= 1.0f)
            {
                fVolume -= 1.0f;
            }
            else
            {
                fVolume += 0.1f;
            }
        }
        else if (chKey == DIK_LEFT)
        {
            if (fVolume <= 0.01f)
            {
                fVolume += 1.0f;
            }
            else
            {
                fVolume -= 0.1f;
            }
        }

        CSoundManager::getInstance()->setMasterVolume(fVolume);
        *(reinterpret_cast<unsigned short*>(param.vpValue)) = 
            static_cast<signed short>(fVolume * 100.0f);
        pOption->setActive(true);
        CApplication::getInstance()->updateVolume(fVolume);
    }

    void mute(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        utils::PARAM &param, unsigned char chKey)
    {
        *(reinterpret_cast<bool*>(param.vpValue)) = 
            CSoundManager::getInstance()->isMuted();
        CSoundManager::getInstance()->setMute(
            !CSoundManager::getInstance()->isMuted());

        pOption->setActive(true);

        CApplication::getInstance()->setMute(
            CSoundManager::getInstance()->isMuted());
    }

    void back(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        utils::PARAM &param, unsigned char chKey)
    {
        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
        {
            CUI::getInstance()->getChild("instructions")->hide();
            CUI::getInstance()->getChild("credits")->hide();
            CUI::getInstance()->getChild("background")->show();
            pMenu->setActive(false);
            CMenuContainer::getInstance()->getMenu("main_menu")->setActive(
                true);
        }
    }
}

namespace Back
{
    void back(CMenu *pMenu, COption *pOption, DELAY_PARAMS &params,
        unsigned char chKey)
    {
        if (chKey == DIK_NUMPADENTER || chKey == DIK_RETURN)
        {
            CUI::getInstance()->getChild("instructions")->hide();
            CUI::getInstance()->getChild("credits")->hide();
            CUI::getInstance()->getChild("background")->show();
            pMenu->setActive(false);
            CMenuContainer::getInstance()->getMenu("main_menu")->setActive(
                true);
        }
    }
}

CApplication::CApplication()
    :  m_pBombermanVocal(nullptr),
     m_pMenuAmbient(nullptr),
     m_pD3D(NULL)
{
}

CApplication *CApplication::getInstance()
{
    /**
     * @return CApplication *A pointer to the static singleton instance.
     */
    static CApplication s_instance;
    return &s_instance;
}

CApplication::~CApplication()
{
    /**
     * Destructor, ensure that all the dynamic memory is reallocated to the
     * heap.
     */
    UnregisterClass("bomberman", m_hinstMainInstance);

    if (m_pD3D != nullptr)             
    {
        m_pD3D->Release();
        m_pD3D = nullptr;
    }
}

LRESULT CApplication::handleWindowMessage(HWND hWnd, UINT msg, WPARAM wParam, 
                                          LPARAM lParam)
{
    /**
     * Handles a message sent from a window onto the event message queue by
     * windows.
     *
     * @param HWND hWnd The window to send the message
     * @param WPARAM wParam The wide parameter, this is the message that was
     *                      sent
     * @param LPARAM lParam Any additional event information for the message.
     */
    switch (msg) 
    {
        case WM_DESTROY:
             PostQuitMessage(0);
             break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

void CApplication::startMusic()
{
    /**
     * Starts the music playing for the application.
     */
    CSoundManager *pSounds(CSoundManager::getInstance());
    CSound *pBombermanSound(pSounds->getSound("bomberman.wav"));
    CSound *pOpeningSound(pSounds->getSound("opening.mp3"));

    pOpeningSound->createNewChannel(&m_pMenuAmbient);

    m_pMenuAmbient->setLoopCount(-1);
    m_pMenuAmbient->setMode(FMOD_LOOP_NORMAL);
    m_pMenuAmbient->setPaused(false);

    pBombermanSound->createNewChannel(&m_pBombermanVocal);
    m_pBombermanVocal->setPaused(false);
}

void CApplication::stopMusic()
{
    m_pMenuAmbient->stop();
    m_pBombermanVocal->stop();
}

bool CApplication::initialise(HINSTANCE hInst)
{
    /**
     * Initialises the application. This creates the window, sets up the
     * directx device context, sets up the cameras, lights and geometry.
     *
     * @return bool Whether or not the device was initialised successfully
     */
    m_hinstMainInstance = hInst;
    createWindow();
    createInputDevices();
    if (FAILED(setupDirectX()))
    {
        return false;
    }
    loadLights();
    loadFonts();
    loadSprites();
    loadSounds();
    loadMenus();
    startMusic();
    CCamera *pCamera(CCamera::getInstance());
    pCamera->setup(CDeviceManager::getInstance()->getDevice());
    pCamera->applyOrthographicMatrix(m_hwndMainWindow);
    pCamera->applyViewMatrix();
    pCamera->applyProjectionMatrix();
    ShowWindow(m_hwndMainWindow, SW_SHOWDEFAULT);
    UpdateWindow(m_hwndMainWindow);

    CMapLoader loader;
    CMapContainer::getInstance()->setCurrentMap(
        loader.loadMap("maps/demo/test.bmm", true));

    return true;
}

void CApplication::createWindow()
{
    /**
     * Creates the window which will hold the directx device control. This sets
     * up the window coordinates, specifying the style and size of the window.
     */
    WNDCLASSEX windowClass;
    windowClass.cbSize = sizeof(WNDCLASSEX);
    windowClass.style = CS_CLASSDC;
    windowClass.lpfnWndProc = Application::handleWindowMessageProc;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = m_hinstMainInstance;
    //windowClass.hIcon = nullptr;
    windowClass.hIcon = LoadIcon(m_hinstMainInstance, 
        MAKEINTRESOURCE(IDI_ICON1));
    windowClass.hCursor = nullptr;
    windowClass.hbrBackground = nullptr;
    windowClass.lpszMenuName = nullptr;
    windowClass.lpszClassName = "bomberman";
    windowClass.hIconSm = nullptr;
    RegisterClassEx(&windowClass);

    m_hwndMainWindow = CreateWindow(
            "bomberman",               // window class name
            "Bomberman",               // title
            WS_OVERLAPPEDWINDOW,       // style
            Constants::WINDOW_START_X, // x start
            Constants::WINDOW_START_Y, // y start
            Constants::WINDOW_WIDTH,   // width
            Constants::WINDOW_HEIGHT,  // height
            GetDesktopWindow(),        // parent window
            nullptr,                   // menu handle
            windowClass.hInstance,     // instance handle
            nullptr                    // parameters
        );
}

void CApplication::createInputDevices()
{
    /**
     * Creates the input devices which will be used in this application. For
     * now, we will only load one keyboard and one mouse. Also initialises
     * the direct input device.
     */
    CInputDeviceManager *pDeviceManager(CInputDeviceManager::getInstance());
    pDeviceManager->setUp(m_hinstMainInstance, m_hwndMainWindow);

    CKeyboard *pKeyboard(new CKeyboard());
    pKeyboard->registerEventListener(&(Application::keyboardCallback));
    pDeviceManager->addDevice("keyboard1", pKeyboard);

    CMouse *pMouse(new CMouse());
    pMouse->registerEventListener(&(Application::mouseCallback));
    pDeviceManager->addDevice("mouse1", pMouse);    
}

HRESULT CApplication::setupDirectX()
{
    /**
     * Creates a D3D object and assigns a direct d3d device to the window
     * context. This sets up the directx control and puts it in the main
     * application window.
     *
     * @return HRESULT Whether or not the directx device was set up successfully
     */

    LPDIRECT3DDEVICE9 pDevice;

    // Create the D3D object.
    m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (m_pD3D == nullptr)
    {
        MessageBox(m_hwndMainWindow, "Cannot create D3D object",
                   "Application::setupDirectX", MB_OK);
        return E_FAIL;
    }

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

    // Create the D3DDevice
    if (FAILED(m_pD3D->CreateDevice(
                D3DADAPTER_DEFAULT, 
                D3DDEVTYPE_HAL, 
                m_hwndMainWindow,
                D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                &d3dpp, 
                &pDevice)))
    {
        MessageBox(m_hwndMainWindow, "Cannot create DirectX device",
                   "Application::setupDirectX", MB_OK);
        return E_FAIL;
    }

    // Enable the Z buffer, since we're dealing with 3D geometry.
    pDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

    pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

    // Enable the light
    pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

    CDeviceManager::getInstance()->setDevice(pDevice);

    return S_OK;
}

void CApplication::loadLights()
{
    /**
     * Loads all the lights used in this program.
     */
    CLight *pLight(CLightContainer::getInstance()->addLight("sunlight"));
    pLight->setType(D3DLIGHT_DIRECTIONAL);

    // More red = morning "red" sun
    // More blue = midnight "blue" moon
    // Green makes things look mossy
    // Good settings are red = green = (blue + 0.1f)
    pLight->setAmbient(0.9f, 0.9f, 0.8f, 0.0f);
    pLight->setDiffuse(0.9f, 0.9f, 0.8f, 0.0f);
    pLight->setSpecular(1.0f, 1.0f, 1.0f, 0.0f);

    pLight->setPosition(288.0f, 256.0f, -720.0f);

    D3DXVECTOR3 vecDir(0.0f, -0.3f, 0.5f);
    D3DXVec3Normalize(&vecDir, &vecDir);
    pLight->setDirection(vecDir);

    pLight->setRange(100.0f);
    CLightContainer::getInstance()->setLight("sunlight", 0, 
        CDeviceManager::getInstance()->getDevice());
    CLightContainer::getInstance()->enableLight(0, 
        CDeviceManager::getInstance()->getDevice());

    pLight = CLightContainer::getInstance()->addLight("spotlight");
    pLight->setType(D3DLIGHT_SPOT);
    pLight->setPosition(0.0f, 0.0f, 0.0f);
    pLight->setAmbient(0.1f, 1.0f, 0.1f, 1.0f);
    pLight->setDiffuse(0.2f, 1.0f, 0.2f, 1.0f);
    pLight->setSpecular(0.0f, 0.0f, 0.0f, 1.0f);
    pLight->setDirection(0, -1, 0);
    pLight->setRange(1000.0f);
    pLight->setTheta(D3DXToRadian(0.0f));
    pLight->setPhi(D3DXToRadian(15.0f));
    pLight->setFalloff(1.0f);
    pLight->setAttenuation(1.0f, 0.0f, 0.0f);;

    CLightContainer::getInstance()->setLight("spotlight", 1, 
        CDeviceManager::getInstance()->getDevice());
    CLightContainer::getInstance()->enableLight(1, 
        CDeviceManager::getInstance()->getDevice());
}

void CApplication::loadFonts()
{
    /**
     * Load all the fonts to be used in the game. We must also load multiple
     * fonts if they are of different sizes.
     */
    CFont *pFont(
        CFontManager::getInstance()->createFont("bomberman", "bm.ttf"));

    CFont *pFont2(
        CFontManager::getInstance()->createFont("bombermansmall", "bomberman",
        "bm.ttf", 36));

    CFont *pFont3(
        CFontManager::getInstance()->createFont("bombermantiny", "bomberman",
        "bm.ttf", 12));
}

void CApplication::loadSprites()
{
    /**
     * Loads the sprites needed for the application class. This consists of
     * all the menu sprites and the main background.
     */
    CUISprite *pBackgroundUI(new CUISprite("textures/ui/menus/title.png"));
    pBackgroundUI->show();
    CUI::getInstance()->addChild("background", pBackgroundUI);

    CUISprite *pInstructions(new CUISprite("textures/ui/menus/instructions.png"));
    CUI::getInstance()->addChild("instructions", pInstructions);
    pInstructions->hide();
    pInstructions->setMarginTop(-20);
    pInstructions->setScale(0.9f, 0.9f);

    CUISprite *pCredits(new CUISprite("textures/ui/menus/credits.png"));
    CUI::getInstance()->addChild("credits", pCredits);
    pCredits->hide();
    pCredits->setMarginTop(-20);
    pCredits->setScale(0.9f, 0.9f);
}

void CApplication::loadSounds()
{
    /**
     * Loads all the sound resources into memory so they can be quickly 
     * accessed at a later stage.
     */
    CSoundManager *pSounds(CSoundManager::getInstance());

    pSounds->addSound("bomberman.wav");
    pSounds->addStream("opening.mp3");
}

void CApplication::loadMenus()
{
    /**
     * Loads all the menus and registers parameters / callbacks.
     */
    CFont *pFont(CFontManager::getInstance()->getFont("bomberman"));

    CMenu *pMenu(CMenuContainer::getInstance()->addMenu("main_menu", 100, 400));
    COption *pOption(pMenu->addOption("Start Game", 0, 0));
    pOption->setFunction(&Application::startGame);
    pOption->setFont(pFont);

    pOption = pMenu->addOption("Instructions", 0, 50);
    pOption->setFunction(&Application::instructions);
    pOption->setFont(pFont);

    pOption = pMenu->addOption("Options", 0, 100);
    pOption->setFunction(&Application::options);
    pOption->setFont(pFont);

    pOption = pMenu->addOption("Credits", 0, 150);
    pOption->setFunction(&Application::credits);
    pOption->setFont(pFont);

    pOption = pMenu->addOption("Exit", 0, 200);
    pOption->setFunction(&Application::exit);
    pOption->setFont(pFont);

    pMenu->setActive(true);

    // Pause menu

    CMenu *pPauseMenu(CMenuContainer::getInstance()->addMenu(
        "pause_menu", 420, 250));
    pPauseMenu->setActiveColour(255, 0, 246, 180);
    pPauseMenu->setInactiveColour(255, 255, 255, 130);

    pPauseMenu->setTitle("Paused", 0.0f, -50.0f);

    pOption = (pPauseMenu->addOption("Continue", 0, 0));
    pOption->setFunction(&PauseCallbacks::continue_);
    pOption->setFont(pFont);

    pOption = pPauseMenu->addOption("Restart", 0, 50);
    pOption->setFunction(&PauseCallbacks::restart);
    pOption->setFont(pFont);

    pOption = pPauseMenu->addOption("Exit", 0, 100);
    pOption->setFunction(&PauseCallbacks::exit);
    pOption->setFont(pFont);

    // Winner menu
    CMenu *pWinnerMenu(CMenuContainer::getInstance()->addMenu(
        "winner_menu", 420, 250));
    pWinnerMenu->setActiveColour(255, 0, 246, 180);
    pWinnerMenu->setInactiveColour(255, 255, 255, 130);

    pWinnerMenu->setTitle("Player X Wins", 0.0f, -150.0f);

    pOption = pWinnerMenu->addOption("Next Round", 0, 0);
    pOption->setFunction(&PauseCallbacks::restart);
    pOption->setFont(pFont);

    pOption = pWinnerMenu->addOption("Quit", 0, 50);
    pOption->setFunction(&PauseCallbacks::exit);
    pOption->setFont(pFont);

    // Map Menu
    CMapMenu *mapMenu(new CMapMenu("maps", 100, 400));
    CMenuContainer::getInstance()->addMenu("map_menu", mapMenu);

    // Options menu
    CMenuValue *pOptions(new CMenuValue(100, 400));
    //pOptions->setTitle("Press ENTER or RETURN to\nchange the value", 0.0f, -100.0f);
    CMenuContainer::getInstance()->addMenu("option_menu", pOptions);

    unsigned short int *pnVolume(new unsigned short int(30));
    bool *pbVolumeOn(new bool(true));

    utils::PARAM volumeParam = {pnVolume, utils::TYPE_USHORT, sizeof(short)};
    utils::PARAM activeParam = {pbVolumeOn, utils::TYPE_BOOL, sizeof(bool)};

    CValueOption *pValueOption(
        pOptions->addOption("Volume Up", 0, 0, volumeParam));
    pValueOption->setFunction(&Options::volume);
    pValueOption->setFont(pFont);

    pValueOption = pOptions->addOption("Toggle Volume", 0, 50, activeParam);
    pValueOption->setFunction(&Options::mute);
    pValueOption->setFont(pFont);

    pValueOption = pOptions->addOption("Back", 0, 150);
    pValueOption->setFunction(&Options::back);
    pValueOption->setFont(pFont);


    CMenu *pBack(CMenuContainer::getInstance()->addMenu(
        "back_menu", 10, 620));
    pOption = pBack->addOption("Back", 0, 0);
    pOption->hide();
    pOption->setFunction(&Back::back);
    pOption->setFont(pFont);
}



void CApplication::loadMap(string szMapName)
{
    /**
     * Loads a map. Executed when a player presses the return key on the play
     * game.
     */
    CMapContainer *pMaps(CMapContainer::getInstance());

    if (pMaps->isMapLoaded())
    {
        pMaps->unloadCurrentMap();
    }

    CMapLoader mapLoader;
    //CMap *pMap(mapLoader.loadMap("maps/test.bmm"));
    CMap *pMap(mapLoader.loadMap(szMapName));
    pMaps->setCurrentMap(pMap);

    if (m_pMenuAmbient != nullptr)
    {
        m_pMenuAmbient->stop();
    }
    if (m_pBombermanVocal != nullptr)
    {
        m_pBombermanVocal->stop();
    }
}

void CApplication::gameLoop()
{
    /**
     * Executes the game loop. This takes a message from the message queue
     * and processes the message. This also calls everything which needs to
     * occur each frame, including the render method.
     */
    MSG windowsMessage;
    ZeroMemory(&windowsMessage, sizeof(windowsMessage));
    
    while (windowsMessage.message != WM_QUIT)
    {
        if (PeekMessage(&windowsMessage, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&windowsMessage);
            DispatchMessage(&windowsMessage);
        }
        else
        {
            // Each game loop

            // Handle any user input
            CInputDeviceManager::getInstance()->processStates();

            // Tick all objects in the world
            tick();
            
            // Render objects to screen
            render();
        }
    }
}

void CApplication::tick()
{
    /**
     * Ticks all the required objects. A tick is just a 'frame' and repetitive
     * checks which need to be checked in each frame should be placed in here.
     * This splits the logic from the render loop, as we are not actually
     * rendering, just changing the logic of the game somehow.
     */

    // Tick all entities which are in the entity container
    CEntityContainer::getInstance()->tick();

    if (CMapContainer::getInstance()->isMapLoaded())
    {
        // Tick the map
        CMapContainer::getInstance()->getCurrentMap()->tick();
    }
}

void CApplication::render() const
{
    /**
     * Clears the back buffer, applies the matrices to render the models and
     * finally present the data to the directx back buffer.
     */
    LPDIRECT3DDEVICE9 pDevice(CDeviceManager::getInstance()->getDevice());

    if (pDevice == nullptr)
    {
        return;
    }

    // Clear the back buffer to a blue colour, also clear the Z buffer at the 
    // same time.
    pDevice->Clear(0, NULL, 
                     D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
                     D3DCOLOR_XRGB(70, 70, 100), 1.0f, 0);

    pDevice->BeginScene();

    // Render the models
    CCamera::getInstance()->setup3D();

    D3DXMATRIX WorldMat, matScale, matTranslate;

    D3DXMatrixIdentity(&WorldMat);
    D3DXMatrixScaling(&matScale, 500.0f, 500.0f, 500.0f);
    D3DXMatrixTranslation(&matTranslate, Constants::CAMERA_POSITION.x,
        Constants::CAMERA_POSITION.y,
        Constants::CAMERA_POSITION.z);
    D3DXMatrixMultiply(&matTranslate, &matScale, &matTranslate);

    pDevice->SetTransform(D3DTS_WORLD, &matTranslate);

    pDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);
    pDevice->SetRenderState(D3DRS_LIGHTING, false);
    pDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    pDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    pDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

    CSkyBox::getInstance()->render();

    pDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
    pDevice->SetRenderState(D3DRS_LIGHTING, true);
    pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
    pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);

    //D3DXMatrixMultiply(&matTranslate, &matTranslate, &matScale);
    pDevice->SetTransform(D3DTS_WORLD, &WorldMat);
        
    CMap *pMap(CMapContainer::getInstance()->getCurrentMap());
    if (pMap != nullptr)
    {
        pMap->render();
    }

    // Render all the meshes currently loaded.
    CDisplayableContainer::getInstance()->render();        

    pDevice->SetTransform(D3DTS_WORLD, &WorldMat);

    // Render the UI
    CCamera::getInstance()->setup2D();


    // Render all the sprites
    //CSpriteManager::getInstance()->render();
    CUI::getInstance()->render();

    // Render all the menus
    CMenuContainer::getInstance()->render();

    // End the scene.
    pDevice->EndScene();

    // Present the back buffer to the display.
    pDevice->Present(NULL, NULL, NULL, NULL);
    
}

void CApplication::updateVolume(float fMasterVolume)
{
    /**
     * Updates the volume to a new master volume
     */
    m_pMenuAmbient->setVolume(fMasterVolume);
}

void CApplication::setMute(bool bMute)
{
    /**
     * Updates the mute value to a new value
     */
    m_pMenuAmbient->setMute(bMute);
}